package com.zz.springboot.config;


import com.zz.springboot.shiro.UserRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro配置类
 * @tips 这个类只有在SpringBoot项目时加载一次，后面的请求将不会进入这个配置类，因为它不是一个拦截器
 */
@Configuration
public class ShiroConfig {

    /**
     * 1.配置 Realm
     */
    @Bean
    public UserRealm userRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {
        UserRealm userRealm = new UserRealm();
        // 设置凭证匹配器
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return userRealm;
    }

    /**
     * 配置凭证匹配器 Bean（用于密码加密比对）（为了匹配登录时的密码加密算法）
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("SHA-256"); // 加密算法
        matcher.setHashIterations(1024);         // 迭代次数（需与注册时一致）
        return matcher;
    }

    /**
     * 2.配置 SecurityManager
     */
    @Bean
    public SecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(userRealm);
        return manager;
    }

    /**
     * 3.配置 Shiro 过滤器（可选）
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
        factory.setSecurityManager(securityManager);

        // 可配置拦截规则（如 /admin/** = authc）
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 1）登录和注册接口不拦截
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/register", "anon");

        // 2）编程式权限控制：基于 权限/角色 进行权限校验
         //①基于权限控制：既需要在数据库中维护用户与角色关系，也需要维护角色与权限的关系
//        filterChainDefinitionMap.put("/test1", "authc,perms[func1]");
//        filterChainDefinitionMap.put("/test2", "authc,perms[func2]");
//
//        //②基于角色控制：只需要在表中维护用户与角色的关系即可，不需要维护角色与权限的关系
//        //写法1：要求用户同时具备member和admin角色，才能访问test3接口
//        //filterChainDefinitionMap.put("/test3", "authc,roles[member,admin]");
//        //写法2：要求用户具备member或admin角色，才能访问test3接口
//        filterChainDefinitionMap.put("/test3", "authc,roles[member]");
//        filterChainDefinitionMap.put("/test3", "authc,roles[admin]");
//
//        filterChainDefinitionMap.put("/test4", "authc,roles[admin]");

        // 3）其它接口都拦截
        filterChainDefinitionMap.put("/**", "authc");

        factory.setFilterChainDefinitionMap(filterChainDefinitionMap);
        factory.setLoginUrl("/login"); // 登录接口路径（可选）
        return factory;
    }

    // -----------------------------------------------------------------------------------------------------------------
    // | 如果需要使用 @RequiresRoles、@RequiresPermissions 来实现授权校验，则必须配置下面的两个bean
    // ----------------------------------------------------------------------------------------------------------------

    /**
     * 创建DefaultAdvisorAutoProxyCreator Bean
     * @desc 用于自动创建被Shiro注解（如 @RequiresRoles、@RequiresPermissions）标记的方法的代理对象。
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true); //表示使用 CGLIB 代理目标类，而不是 JDK 动态代理接口，确保即使没有接口的类也能被代理
        return advisorAutoProxyCreator;
    }

    /**
     *  创建AuthorizationAttributeSourceAdvisor Bean
     *  @desc 将 Shiro 的权限注解（如 @RequiresRoles、@RequiresPermissions）与 Shiro 的安全机制集成。
     *        通过传入的 securityManager 获取当前用户的权限信息，在方法执行前进行权限校验
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


}
